#pragma once

#include <algorithm>
#include <hash_map>
#include <vector>
#include "Calls.h"


using namespace stdext;
using namespace std;


Calls::Calls(void){
	callBitMapSetFlag = false;
}
Calls::~Calls(void){
	Calls::callRelation.clear();
	Calls::revertCallRelation.clear();
	number_of_procedure = 1;
	Calls::callBitMap.resize(number_of_procedure);
	for (int i=0; i<number_of_procedure; i++){
		callBitMap.at(i).resize(number_of_procedure);
	}
}


//Set the first Proc calls the second Proc, with the specific line index who calls the second proc.
//return false, if not set successfully
bool Calls::setCalls(Type::Index::Proc callerProc, Type::Index::Line callerLine, Type::Index::Proc calledProc){
	//insert to callRelation
	if (callRelation.find(callerProc) != callRelation.end()){
		callRelation.find(callerProc)->second.push_back(calledProc);
	}else{
		vector<Type::Index::Proc> listOfCalledProc;
		listOfCalledProc.push_back(calledProc);
		callRelation[callerProc] = listOfCalledProc;
	}
	//insert to revertCallRelation
	if (revertCallRelation.find(calledProc) != revertCallRelation.end()){
		revertCallRelation.find(calledProc)->second.push_back(callerProc);
	}else{
		vector<Type::Index::Proc> listOfCallerProc;
		listOfCallerProc.push_back(callerProc);
		revertCallRelation[calledProc] = listOfCallerProc;
	}
	//insert to callRelationLineToProc
	if (callRelationLineToProc.find(callerLine) != callRelationLineToProc.end()){
		return false;
	}else{
		callRelationLineToProc[callerLine] = calledProc;
	}
	//insert to revertCallRelationProcToLine
	if (revertCallRelationProcToLine.find(calledProc) != revertCallRelationProcToLine.end()){
		revertCallRelationProcToLine.find(calledProc)->second.push_back(callerLine);
	}else{
		vector<Type::Index::Line> listOfCallerLine;
		listOfCallerLine.push_back(callerLine);
		revertCallRelationProcToLine[calledProc] = listOfCallerLine;
	}
	//insert to allCallLineInProc
	if (allCallLineInProc.find(callerProc)!=allCallLineInProc.end()){
		allCallLineInProc.find(callerProc)->second.push_back(callerLine);
	}else{
		Type::List::Line allCallStmts;
		allCallStmts.push_back(callerLine);
		allCallLineInProc[callerProc] = allCallStmts;
	}
	return true;
}
		
//return a list of all procedure indexes called by a specified index
//if none, return an empty vector
//eg. Calls(1,2) Calls(1,3) ---> getCalledBy(1) will return 2,3
vector<Type::Index::Proc> Calls::getCalledBy(Type::Index::Proc callerProc){
	vector<Type::Index::Proc> calledProcList;
	if (callRelation.find(callerProc) != callRelation.end()) {
		return callRelation.find(callerProc)->second;
	} 
	return calledProcList;
}

//return a list of all procedure indexes called by a specified lineIndex
//if none, return -1
//eg. Calls(1,2) and line 5 in procefure 1 calls procedure 2.---> getCalledByLine(5) will return 2
Type::Index::Proc Calls::getCalledByLine(Type::Index::Line callerLine){
	Type::Index::Proc calledProc = -1;
	if (callRelationLineToProc.find(callerLine) != callRelationLineToProc.end()) {
		return callRelationLineToProc.find(callerLine)->second;
	} 
	return calledProc;
}

//return a list of all procedure indexes that call a specified index
//if none, return an empty vectore
//eg. Calls(1,3) Calls(2,3) ---> getCallerOf(3) will return 1,2
vector<Type::Index::Proc> Calls::getCallerOf(Type::Index::Proc calledProc){
	vector<Type::Index::Proc> callerProcList;
	if (revertCallRelation.find(calledProc) != revertCallRelation.end()) {
		return revertCallRelation.find(calledProc)->second;
	} 
	return callerProcList;
}

//return a list of all line indexes that call a specified procIndex
//if none, return an empty vector
//eg. Calls(1,3) Calls(2,3) where line 5, line 8 in procedure 1 and line 9 in procedure 2 call procedure 3 ---> getCallerLineOf(3) will return 5,8,9
vector<Type::Index::Line> Calls::getCallerLineOf(Type::Index::Proc calledProc){
	vector<Type::Index::Line> callerLineList;
	if (revertCallRelationProcToLine.find(calledProc) != revertCallRelationProcToLine.end()) {
		return revertCallRelationProcToLine.find(calledProc)->second;
	}
	return callerLineList;
}
		
//return true if the first Proc calls the second Proc
bool Calls::isCalls(Type::Index::Proc callerProc, Type::Index::Proc calledProc){
	if (Calls::callBitMapSetFlag){
		return callBitMap.at(callerProc).at(calledProc);
	}else{
		Type::List::Proc procList = getCalledBy(callerProc);
		for (Type::List::Proc::iterator iter = procList.begin(); iter!= procList.end(); ++iter){
			if (*iter == calledProc){
				return true;
			}
		}
		return false;
	}
}

//return true if the bitmap is setted correctly
bool Calls::setCallsBitMap(vector<vector<bool>> CBM){
	if (CBM.empty()){
		return false;
	}
	int proSize = CBM.size();
	callBitMap.resize(proSize, vector<bool>(proSize,false));
	for (int i=0; i<proSize;i++){
		for (int j=0; j<proSize;j++){
			callBitMap.at(i).at(j) = CBM.at(i).at(j);
		}
	}
	Calls::callBitMapSetFlag = true;
	return true;
}
vector<Type::Index::Line> Calls::getCallStmtOfProc(Type::Index::Proc p){
	if (Calls::allCallLineInProc.find(p)==Calls::allCallLineInProc.end()){
		return vector<Type::Index::Line>();
	}else{
		return Calls::allCallLineInProc.at(p);
	}
}